package com.yxw.bamboo.core.send.event.mssql;

import com.yxw.bamboo.core.BytesHelper;
import com.yxw.bamboo.core.NetPortForward;
import com.yxw.bamboo.core.SocketChannel;
import com.yxw.bamboo.core.database.mssql.MsBufferWrite;
import com.yxw.bamboo.core.database.mysql.MySqlConfig;
import com.yxw.bamboo.core.database.mssql.TDSType;
import com.yxw.bamboo.core.database.sql.DbLog;
import com.yxw.bamboo.core.database.sql.LogData;
import com.yxw.bamboo.core.database.sql.Mock;
import com.yxw.bamboo.core.database.sql.MockData;
import com.yxw.bamboo.core.send.event.SendEvent;
import com.yxw.bamboo.core.send.event.EventArgs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.yxw.bamboo.DecodeSSL;


import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class MsSqlDefaultBeforeSendEvent implements SendEvent {

    private final static Logger logger = LoggerFactory.getLogger(MsSqlDefaultBeforeSendEvent.class);
    @Autowired
    MsBufferWrite msBufferWrite;
    @Autowired
    DbLog dbLog;

    @Autowired
    DecodeSSL decodeSSL;
    @Override
    public void process(Object eventSource, EventArgs eventArgs) throws Exception {

        NetPortForward netPortForward=(NetPortForward)eventSource;
        SocketChannel channel=netPortForward.getChannel();
        byte[] des=(byte[])eventArgs.getArg();
        String[] sqlPWDPluginNames= MySqlConfig.getMySqlPwdPlugin();
        byte cryptoMeta=0;
        byte[] sqlCollationBytes=new byte[5];

        if(channel.getChannelName().equals(SocketChannel.LOCAL_TO_REMOTE))
        {
            Integer offset=0;
            if(channel.getSocketStep()>=1)
            {
                if(channel.getSocketStep()>4)
                {
                    logger.info(Arrays.toString(des));
                    if(des.length<=45)
                    {

                        return;
                    }

                    //sql语句长度小于8000是69位 如果大于8000是71位 且没有参数
                    //des 0号位置标示的是客户端请求类型 1标示的是jdbcTemplate 请求的 已经解析好的sql 语句
                    //3 标示的是 带参数的sql 语句
                    boolean bOut=false;
                    int count=69;
                    String sql="";
                    byte[] newBytes=null;

                    int tdsMessageType=(int)des[0];

                    //sql语句长度
                    int sqlLength=-1;
                     int sqlStartIndex=-1;
                     int sqlEndIndex=-1;

                    logger.info("tdsMessageType:"+tdsMessageType);
                    //无参数情况出现
                    if(tdsMessageType==1)
                    {
                        count=30;
                    }else if(tdsMessageType==3)
                    {
                        int sqlLenghtCount=2;
                        //带参数的前45固定位去掉存放的客户端基本信息，45之后存放 参数名称 类型 和sql语句以及 参数值
                        count=45;
                        //46位表示的十参数名称长度
                        count++;
                        //参数长度
                        int nameLength=(int)(des[count]&0xff);
                        if(nameLength>0)
                        {
                            int skipLength=nameLength*2;
                            count=count+skipLength;
                        }
                        int bOutInt=(int)des[count];
                        cryptoMeta=des[count];
                        bOut=(bOutInt==1||bOutInt==9);
                        count++;
                        int dbType=des[count]&0xff; //48
                        TDSType tdsType=TDSType.valueOf(dbType);

                        boolean hasParms=false;
                        //变量名称和变量类型基本不会大于8000所以不考虑
                        if(bOut)
                        {
                            //count=count+15;
                            count=count+2;
                            System.arraycopy(des,count,sqlCollationBytes,0,sqlCollationBytes.length);
                            count=count+5;

                            //
                            byte[] lengthMaxByts=new byte[8];
                            System.arraycopy(des,count,lengthMaxByts,0,8);
                            long maxLength=BytesHelper.bytesToLongLittle(lengthMaxByts,0);
                            count=count+8;
                            if(maxLength!=-1L)
                            {
                                //参数和参数类型字符串长度
                                byte[] lengthByts=new byte[4];
                                System.arraycopy(des,count,lengthByts,0,4);

                                int length=BytesHelper.bytesToIntLittle(lengthByts,0);
                                count+=4;
                                count+=length;
                                hasParms=true;
                            }else
                            {
                                count+=4;
                            }
                        }else
                        {
                            count=count+2;
                            System.arraycopy(des,count,sqlCollationBytes,0,sqlCollationBytes.length);
                            count=count+5;
                            count++;
                            byte[] lengthByts=new byte[2];
                            System.arraycopy(des,count,lengthByts,0,2);
                            short length=BytesHelper.bytesToShortLittle(lengthByts,0);
                            count+=2;
                            if(length!=-1)
                            {
                                count+=length;
                                hasParms=true;
                            }

                        }
                        //分析具体sql语句
                        //参数长度
                        sqlStartIndex=count;
                        count++; //109
                        int sqlTypeLength=(int)(des[count]&0xff);
                        if(sqlTypeLength>0)
                        {
                            int skipLength=sqlTypeLength*2;
                            count=count+sqlTypeLength;
                        }
                        count=count+2;
                        //112
                        //分析sql语句长度
                        //如果小于8000,des[count]==8000如果不等于80000则取4 和2147483647比较 如果都不是则表示走的usePLP 逻辑
                        byte[] sqlMaxLengthByts=new byte[2];
                        System.arraycopy(des,count,sqlMaxLengthByts,0,2);
                        short sqlMaxLength=BytesHelper.bytesToShortLittle(sqlMaxLengthByts,0);
                        if(sqlMaxLength==8000)
                        {
                            count+=2; //114
                            System.arraycopy(des,count,sqlCollationBytes,0,sqlCollationBytes.length);
                            count+=5;
                        }else //大于8000或者 plp
                        {
                            sqlLenghtCount=4;
                            sqlMaxLengthByts=new byte[4];
                            System.arraycopy(des,count,sqlMaxLengthByts,0,4);
                            int sqlMaxLengthInt=BytesHelper.bytesToIntLittle(sqlMaxLengthByts,0);
                            if(sqlMaxLengthInt==2147483647)
                            {
                                count+=4;
                                System.arraycopy(des,count,sqlCollationBytes,0,sqlCollationBytes.length);
                                count+=5;
                            }else
                            {
                                //count=count+15;
                                count=count+2;
                                System.arraycopy(des,count,sqlCollationBytes,0,sqlCollationBytes.length);
                                count=count+5;
                                count=count+8;

                            }
                        }

                        //有参数的逻辑需要在此返回
                        sqlMaxLengthByts=new byte[sqlLenghtCount];
                        System.arraycopy(des,count,sqlMaxLengthByts,0,sqlLenghtCount);

                        if(sqlLenghtCount==4)
                        {
                            sqlLength=BytesHelper.bytesToIntLittle(sqlMaxLengthByts,0);
                        }else
                        {
                            sqlLength=BytesHelper.bytesToShortLittle(sqlMaxLengthByts,0);
                        }

                        count=count+sqlLenghtCount; //121 sql长度124

                        sqlEndIndex=count;

                    }

                    if(sqlLength>-1)
                    {
                        newBytes=new byte[sqlLength];
                    }else
                    {
                    newBytes=new byte[des.length-count];
                }

                    System.arraycopy(des, count, newBytes, 0, newBytes.length);
                    byte[] covertByts=BytesHelper.bigLittleCovert(newBytes);
                    sql=new String(covertByts,"UTF-16");

                    MockData mockData=Mock.hitMsSql(sql,channel.getDbINfo().getUserName(),channel.getDbINfo().getDbName());

                    offset++;
                    dbLog.addLog(channel.getHostId(),channel.getSocketHashCode(),channel.getSocketStep(),offset,"SQL",sql);

                    if(mockData!=null)
                    {

                        if(tdsMessageType==1)
                        {
                            byte[] expByte=BytesHelper.toUCS16(mockData.getReplaceSql());
                            byte[] newSendByte=new byte[expByte.length+30];
                            System.arraycopy(des, 0, newSendByte, 0, 30);
                            System.arraycopy(expByte, 0, newSendByte, 30, expByte.length);
                            newSendByte[2]=(byte) (newSendByte.length>>8&255);
                            newSendByte[3]=(byte) (newSendByte.length>>0&255);
                            String sendData=new String(newSendByte);
                            logger.info(channel.getSocketHashCode()+"命中---新发送的sql："+sendData);
                            eventArgs.setResult(newSendByte);
                            offset++;
                            dbLog.addLog(channel.getHostId(),channel.getSocketHashCode(),channel.getSocketStep(),offset,"M-SQL",mockData.getReplaceSql());

                        }else if (tdsMessageType==3)
                        {
                            byte[] newSendByte=new byte[57];
                            System.arraycopy(des, 0, newSendByte, 0, 57);
                            newSendByte[55]=(byte) (-1>>8&255);
                            newSendByte[56]=(byte) (-1>>0&255);



                            ByteBuffer bf=ByteBuffer.allocate(500+mockData.getReplaceSql().length()*2).order(ByteOrder.LITTLE_ENDIAN);
                            msBufferWrite.writeBytes(newSendByte,bf);
                            msBufferWrite.writeRPCStringUnicode((String)null,mockData.getReplaceSql(),bOut,sqlCollationBytes,bf,  cryptoMeta);

                            byte[] sendBytes=new byte[bf.position()];

                            System.arraycopy(bf.array(),0,sendBytes,0,sendBytes.length);
                            sendBytes[2]=(byte) (bf.position()>>8&255);
                            sendBytes[3]=(byte) (bf.position()>>0&255);

                            String sendData=new String(sendBytes);
                            logger.info(channel.getSocketHashCode()+"命中---新发送的sql："+sendData);
                            eventArgs.setResult(sendBytes);
                            offset++;
                            dbLog.addLog(channel.getHostId(),channel.getSocketHashCode(),channel.getSocketStep(),offset,"M-SQL",mockData.getReplaceSql());
                        }

                    }

                }

            }
        }else //
        {
            if(channel.getSocketStep()==4)
            {
                String regex = "\\'.*\\'";
                Pattern pattern = Pattern.compile(regex);
                String data=new String(des);
                Matcher matcher = pattern.matcher(data);
                if(matcher.find())
                {
                   channel.getDbINfo().setDbName(matcher.toMatchResult().group(0));
                }

            }

        }
    }
}
